use core::str;
use std::sync::LazyLock;

mod area;

static AREA_NUMBER_MANAGER: LazyLock<area::AreaNumberManager> =
    LazyLock::new(|| area::AreaNumberManager::with_file("area_number.txt"));

pub fn check_id_card(id_card_no: &str) -> String {
    println!("INPUT: {id_card_no}");
    match check_id_card_core(id_card_no) {
        Ok((gender, birthday, area)) => {
            println!("ANS: {:?}", (&gender, &birthday, &area));
            format!("身份证号码正确,{gender},{birthday},{area}")
        }
        Err(_) => "身份证号码错误".to_string(),
    }
}

/// 分析身份证号.
/// 如果身份证号不合法, 返回`Err`, 否则返回 `(性别, 生日, 地区)`.
fn check_id_card_core(id_card_no: &str) -> Result<(String, String, String), ()> {
    let id_card_no = id_card_no.as_bytes().to_vec();
    let id_length = match id_card_no.len() {
        18 => IdLength::Long,
        15 => IdLength::Short,
        _ => {
            return Err(());
        }
    };

    // 地区
    let area_number = area::trans_number_u8(id_card_no.as_slice());
    let area = AREA_NUMBER_MANAGER.get_area(area_number);
    let area = area.join("-");
    // println!("DEBUG: area: {area}");
    if area.is_empty() {
        return Err(());
    }

    let birthday = get_birthday(&id_card_no[6..], &id_length);
    let birthday = format!("{}年{}月{}日", birthday.0, birthday.1, birthday.2);

    let gender_index = match &id_length {
        IdLength::Long => 16,
        IdLength::Short => 14,
    };
    let gender = if id_card_no[gender_index] % 2 == 0 {
        "女"
    } else {
        "男"
    };

    if id_length == IdLength::Long {
        if !check_no(&id_card_no) {
            return Err(());
        }
    }

    Ok((gender.to_string(), birthday, area))
}

/// 身份证长度
#[derive(PartialEq, Eq)]
enum IdLength {
    Long,
    Short,
}

/// 获取生日 (年月日).
fn get_birthday(s: &[u8], id_length: &IdLength) -> (String, String, String) {
    match id_length {
        IdLength::Long => {
            let year = unsafe { str::from_utf8_unchecked(&s[..4]) }.to_string();
            let month = unsafe { str::from_utf8_unchecked(&s[4..6]) }.to_string();
            let day = unsafe { str::from_utf8_unchecked(&s[6..8]) }.to_string();
            (year, month, day)
        }
        IdLength::Short => {
            let year = "19".to_string() + unsafe { str::from_utf8_unchecked(&s[..2]) };
            let month = unsafe { str::from_utf8_unchecked(&s[2..4]) }.to_string();
            let day = unsafe { str::from_utf8_unchecked(&s[4..6]) }.to_string();
            (year, month, day)
        }
    }
}

/// 18位身份证的校验位计算.
fn check_no(s: &[u8]) -> bool {
    let check_range = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    let weight = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];

    let mut sum = 0;
    for i in 0..17 {
        sum += (s[i] - '0' as u8) as usize * weight[i];
    }
    let sum = sum % 11;
    let ans = check_range[sum];
    // println!("DEBUG: target check num: {ans}");

    let aim = s[17];
    aim == ans as u8
}
